/* eslint-disable react-refresh/only-export-components */
import ENV from "@/utils/env"
import { css } from "@emotion/react"
import styled from "@emotion/styled"
import { atom, getDefaultStore, useAtomValue, useSetAtom } from "jotai"
import { atomFamily } from "jotai/utils"
import { AnimatePresence, motion } from "motion/react"
import { useCallback } from "react"

const Container = styled(motion.div)<{ priority: number }>`
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    flex-direction: column;
    z-index: calc(1000 + ${({ priority }) => priority});
    justify-content: flex-end;
    align-items: stretch;

    &[data-position="center"] {
        justify-content: center;
        align-items: center;
    }
`

const gridLineBackgroundCss = css`
    --color: #2962ff;
    --size: 20rem;

    background-image:
        linear-gradient(90deg, var(--color) 1rem, transparent 0),
        linear-gradient(0deg, var(--color) 1rem, transparent 0);
    background-size: var(--size) var(--size);
`

const Modal = styled(motion.div)`
    display: flex;
    flex-direction: column;
    margin: auto 0 0;
    ${ENV.dev_mode ? gridLineBackgroundCss : ""}

    &[data-position="center"] {
        margin: 0 auto;
        align-self: center;
    }
`

type ModalState = {
    isOpen: boolean
    element: React.ReactNode
    options?: {
        closeOnClickMask?: boolean
        closeOnEscape?: boolean
        animation?: "slide-up" | "fade"
        position?: "bottom" | "center"
        activeId?: string
        onClose?: () => void
    }
}

const defaultOptions: ModalState["options"] = {
    closeOnClickMask: true,
    closeOnEscape: true,
    animation: "slide-up",
    position: "bottom",
}

function createAtoms() {
    const isOpenAtom = atom<ModalState["isOpen"]>(false)
    const elementAtom = atom<ModalState["element"]>(null)
    const optionsAtom = atom<ModalState["options"]>(defaultOptions)

    const stateAtom = atom<ModalState>((get) => {
        return {
            isOpen: get(isOpenAtom),
            element: get(elementAtom),
            options: get(optionsAtom),
        }
    })

    const openAtom = atom(
        null,
        (_get, set, element: React.ReactNode, opts?: ModalState["options"]): Promise<void> => {
            return new Promise((resolve) => {
                set(isOpenAtom, true)
                set(elementAtom, element)
                set(optionsAtom, { ...defaultOptions, ...opts })
                setTimeout(resolve, 300)
            })
        },
    )

    const closeAtom = atom(null, (_get, set): Promise<void> => {
        return new Promise((resolve) => {
            set(isOpenAtom, false)
            setTimeout(() => {
                set(elementAtom, null)
                set(optionsAtom, defaultOptions)
                resolve()
            }, 300)
        })
    })
    const atoms = {
        stateAtom,
        isOpenAtom,
        elementAtom,
        optionsAtom,
        openAtom,
        closeAtom,
    }
    return atoms
}

type ClickEvent = React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>

// 背景遮罩動畫
const containerVariants = {
    hidden: {
        backgroundColor: "rgba(0, 0, 0, 0)",
    },
    visible: {
        backgroundColor: "rgba(0, 0, 0, 0.5)",
        transition: {
            duration: 0.2,
        },
    },
    exit: {
        backgroundColor: "rgba(0, 0, 0, 0)",
        transition: {
            duration: 0.2,
            delay: 0.1,
        },
    },
}

// Modal 動畫變體
const modalVariants = {
    "slide-up": {
        hidden: {
            y: "100%",
        },
        visible: {
            y: "0%",
            transition: {
                type: "spring",
                damping: 25,
                stiffness: 300,
            },
        },
        exit: {
            y: "100%",
            transition: {
                type: "tween",
                duration: 0.2,
                ease: "easeInOut",
            },
        },
    },
    fade: {
        hidden: {
            opacity: 0,
            scale: 0.95,
            y: 20,
        },
        visible: {
            opacity: 1,
            scale: 1,
            y: 0,
            transition: {
                type: "spring",
                damping: 25,
                stiffness: 300,
            },
        },
        exit: {
            opacity: 0,
            scale: 0.95,
            y: 20,
            transition: {
                type: "tween",
                duration: 0.2,
                ease: "easeInOut",
            },
        },
    },
}

const stateAtomMap = new Map<string, ReturnType<typeof createAtoms>>()

const atomsMapAtomFamily = atomFamily((name: string) =>
    atom(() => {
        let atoms = stateAtomMap.get(name)
        if (!atoms) {
            atoms = createAtoms()
            stateAtomMap.set(name, atoms)
        }
        return atoms
    }),
)

function ModalSlideUp(props: {
    familyName?: Components.WhereIsTheModalSlideUp
    priority?: number
}) {
    const {
        //
        familyName: _familyName = "root",
        priority = 0,
    } = props

    const familyName = `${_familyName}`

    const atoms = useAtomValue(atomsMapAtomFamily(familyName))
    const state = useAtomValue(atoms.stateAtom)
    const options = useAtomValue(atoms.optionsAtom)
    const animation = options?.animation ?? "slide-up"
    const position = options?.position ?? "bottom"

    const actionClose = useSetAtom(atoms.closeAtom)

    const close = useCallback(() => {
        actionClose()
        options?.onClose?.()
    }, [actionClose, options])

    const onMaskClick = useCallback(
        (ev: ClickEvent) => {
            ev.stopPropagation()

            if (state.options?.closeOnClickMask) {
                close()
            }
        },
        [close, state.options?.closeOnClickMask],
    )

    const onModalClick = useCallback((ev: ClickEvent) => {
        ev.stopPropagation()
    }, [])

    return (
        <AnimatePresence mode="wait">
            {state.isOpen && state.element && (
                <Container
                    className="modal-slide-up-container"
                    onClick={onMaskClick}
                    onTouchEnd={onMaskClick}
                    variants={containerVariants}
                    initial="hidden"
                    animate="visible"
                    exit="exit"
                    data-position={position}
                    priority={priority}
                >
                    <Modal
                        className="modal-slide-up-modal"
                        onClick={onModalClick}
                        onTouchEnd={onModalClick}
                        variants={modalVariants[animation]}
                        initial="hidden"
                        animate="visible"
                        exit="exit"
                        data-position={position}
                    >
                        {state.element}
                    </Modal>
                </Container>
            )}
        </AnimatePresence>
    )
}
const jotai_store = getDefaultStore()

export const ModalSlideUpStaticMethods = {
    open: async (
        name: Components.WhereIsTheModalSlideUp,
        element: React.ReactNode,
        opts?: ModalState["options"],
    ) => {
        const _atoms = jotai_store.get(atomsMapAtomFamily(name))
        await jotai_store.set(_atoms.openAtom, element, opts)
    },
    close: async (name: Components.WhereIsTheModalSlideUp) => {
        const _atoms = jotai_store.get(atomsMapAtomFamily(name))
        await jotai_store.set(_atoms.closeAtom)
    },
    closeAll: async () => {
        for (const atoms of stateAtomMap.values()) {
            await jotai_store.set(atoms.closeAtom)
        }
    },
}

export default ModalSlideUp
export { atomsMapAtomFamily as modalSlideUpAtomsFamily }
